package com.xiaojie.init;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.Connection;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 初始化——构造方法！
 * 初始化——@PostConstruct方法
 * 初始化——InitializingBean接口
 * 初始化——init方法！
 * ---容器启动完毕后...
 * 容器启动—CommandLineRunner接口方法！
 */
@Slf4j
@Component
public class RabbitContextHolder {

    private static ConcurrentHashMap<String, RabbitVirtualHost> vhostMapping = new ConcurrentHashMap<>();

    private static ConcurrentHashMap<String, CachingConnectionFactory> rabbitConnectionMapping = new ConcurrentHashMap<>();

    private static ConcurrentHashMap<String, RabbitAdmin> adminMapping = new ConcurrentHashMap<>();

    private static ConcurrentHashMap<String, RabbitTemplate> templateMapping = new ConcurrentHashMap<>();

    public static List<RabbitVirtualHost> rabbitVirtualHosts = new ArrayList<>();

    /**
     * 初始化的连接配置。
     */
    static {
        RabbitVirtualHost rabbitVirtualHost = new RabbitVirtualHost();
        rabbitVirtualHost.setHost("10.10.16.158");
        rabbitVirtualHost.setPort(5672);
        rabbitVirtualHost.setUsername("vz");
        rabbitVirtualHost.setPassword("Seentao@123");
        rabbitVirtualHost.setVhost("/vz");

//        RabbitVirtualHost r2 = new RabbitVirtualHost();
//        r2.setHost("localhost");
//        r2.setPort(5672);
//        r2.setUsername("guest");
//        r2.setPassword("guest");
//        r2.setVhost("pigeon");
//        rabbitVirtualHosts.add(r2);

        rabbitVirtualHosts.add(rabbitVirtualHost);
    }

    /**
     * 项目启动时，会初始化队列。
     */
//    @PostConstruct
    void initContainer() {
        //读取多个RabbitMq的配置，多个虚拟主机。
        rabbitVirtualHosts.forEach(RabbitContextHolder::addNewVHost);
    }

    /**
     * 根据MQ连接信息去初始化
     * {@link CachingConnectionFactory}
     * {@link RabbitAdmin}
     * {@link RabbitTemplate}
     * 配置并放入到内存中。
     *
     * @param conn RabbitMq的连接信息
     */
    public static void addNewVHost(RabbitVirtualHost conn) {
        //创建连接工厂
        CachingConnectionFactory factory = new CachingConnectionFactory();
        factory.setHost(conn.getHost());
        factory.setConnectionTimeout(5000);
        factory.setPort(conn.getPort());
        factory.setVirtualHost(conn.getVhost());
        factory.setUsername(conn.getUsername());
        factory.setPassword(conn.getPassword());
        //保证生产者不丢消息
        factory.setPublisherReturns(true);
        factory.setPublisherConfirms(true);

        try {
            Connection connection = factory.createConnection();
            if (!connection.isOpen()) {
                log.error("Rabbit的连接工厂创建失败，虚拟主机为[{}]", conn.getVhost());
            }
            RabbitAdmin rabbitAdmin = new RabbitAdmin(factory);
            rabbitAdmin.setAutoStartup(true);
            RabbitTemplate rabbitTemplate = new RabbitTemplate(factory);
            //统一对异常的处理
            rabbitTemplate.setReplyErrorHandler(t -> {
                log.error("进行重试了下哈~");
            });
            //交换器无法根据自身类型和路由键找到一个符合条件的队列时的处理方式
            //true：RabbitMQ会调用Basic.Return命令将消息返回给生产者
            //false：RabbitMQ会把消息直接丢弃
            rabbitTemplate.setMandatory(true);
            //放入到各个缓存中
            rabbitConnectionMapping.put(conn.getVhost(), factory);
            adminMapping.put(conn.getVhost(), rabbitAdmin);
            templateMapping.put(conn.getVhost(), rabbitTemplate);
            vhostMapping.put(conn.getVhost(), conn);
        } catch (Exception e) {
            log.error("初始化connection factory 失败，虚拟主机为[{}]", conn.getVhost(), e);
        }
    }

    /**
     * 移除内存中的配置信息
     *
     * @param vhost 虚拟机对象
     */
    public static void delVHost(String vhost) {
        rabbitConnectionMapping.remove(vhost);
        adminMapping.remove(vhost);
        templateMapping.remove(vhost);
        vhostMapping.remove(vhost);
    }

    public static CachingConnectionFactory getConnectionFactory(String vhost) {
        return rabbitConnectionMapping.get(vhost);
    }

    public static RabbitAdmin getRabbitAdmin(String vhost) {
        return adminMapping.get(vhost);
    }

    public static RabbitTemplate getRabbitTemplate(String vhost) {
        return templateMapping.get(vhost);
    }

    public static RabbitVirtualHost getVHost(String vhost) {
        return vhostMapping.get(vhost);
    }
}
